#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <poll.h>
#include "sock.hpp"

class poll_server
{
    const int N = 100;

public:
    poll_server(uint16_t port = 8888) : _port(port) {}

    ~poll_server() { _listen_sock.Close(); }

    void init()
    {
        _listen_sock.Socket();
        _listen_sock.Bind(_port);
        _listen_sock.Listen();

        // 初始化数组
        _fds = new struct pollfd[N];
        for (size_t i = 0; i < N; ++i)
        {
            _fds[i].fd = -1;
            _fds[i].events = 0;
            _fds[i].revents = 0;
        }

        // 将_listen_sock添加到数组中
        _fds[0].fd = _listen_sock.GetSock();
        _fds[0].events = POLLIN;
    }

    void accepter()
    {
        std::string client_ip;
        uint16_t client_port;
        int sock = _listen_sock.Accept(&client_ip, &client_port);

        // 这时候不能直接读取，需要将sock添加到_fds中，继续select
        int i = 0;
        for(i = 0; i < N; ++i)
        {
            if(_fds[i].fd != -1)
                break;
        }
        if(i == N)
        {
            // 动态扩容...
            std::cout << "数组已满" << std::endl;
            close(sock);
        }
        else
        {
            _fds[i].fd = sock;
            _fds[i].events = POLLIN | POLLOUT;
            _fds[i].revents = 0;
        }
    }

    void recver(int index)
    {
        int sock = _fds[index].fd;
        std::string buffer(1024, 0);
        ssize_t n = recv(sock, &buffer[0], buffer.size(), 0);
        if (n == -1)
        {
            perror("revc fail");
            _fds[index].fd = -1;
            _fds[index].events = 0;
            _fds[index].revents = 0;
            close(sock);
            return;
        }
        else if (n == 0)
        {
            std::cout << "client quit" << std::endl;
            _fds[index].fd = -1;
            _fds[index].events = 0;
            _fds[index].revents = 0;
            close(sock);
            return;
        }
        std::cout << "[request]" << buffer << std::endl;

        // 不考虑write就绪情况，直接写
        std::string response("[server return]");
        response += buffer;
        send(sock, &response[0], response.size(), 0);
    }

    void some_fd_ok()
    {
        for (size_t i = 0; i < N; ++i)
        {
            if (_fds[i].fd != -1)
            {
                if (_fds[i].revents & POLLIN) //读取就绪，数据可读
                {
                    // 读取就绪有两种情况
                    // 一种是_listen_sock接受到了新的连接
                    // 另一种是数据就绪

                    // 1.新的连接
                    if (_fds[i].fd == _listen_sock.GetSock())
                    {
                        std::cout << "新的连接" << std::endl;
                        accepter();
                    }

                    // 2.数据就绪
                    else
                    {
                        std::cout << "数据就绪" << std::endl;
                        recver(i);
                    }
                }
                else if(_fds[i].revents & POLLOUT) //可写就绪，数据可写
                {

                }
            }
        }
    }

    void start()
    {
        while (1)
        {
            // int poll(struct pollfd * fds, nfds_t nfds, int timeout);
            // struct pollfd
            // {
            //     int fd;        /* file descriptor */
            //     short events;  /* requested events */
            //     short revents; /* returned events */
            // };
            // poll将输入参数和输出参数进行了分离，分为events和revents
            // poll的第一个参数可看成一个数组，数组元素为struct pollfd结构体
            // poll的第二个参数可看成数组大小
            // poll的第三个参数单位是毫秒，传入-1表示阻塞等待
            // events事件有：POLLIN表示数据可读，POLLOUT表示数据可写
            // poll的返回值n大于0时，表示有n个fd准备就绪，等于0时表示timeout，小于0表示出错

            int n = poll(_fds, N, -1);
            if (n > 0)
            {
                some_fd_ok();
            }
            else if (n == 0)
            {
                std::cout << "time out" << std::endl;
            }
            else
            {
                perror("select err");
            }
        }
    }

private:
    uint16_t _port;
    sock _listen_sock;
    struct pollfd *_fds;
};